package com.wuad.platformwuad.module.study.activiti7Demo.bank;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskInfo;
import org.apache.commons.lang3.StringUtils;
import org.junit.Test;

import java.io.InputStream;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.zip.ZipInputStream;

/**
 * 银行审批测试
 *
 * @author wuad
 */
@Slf4j
public class BankTest {

    /**
     * 流程标识id
     */
    public static final String PROCESS_DEFINITION_KEY = "myProcess";

    /**
     * 流程部署id
     */
    private String deploymentId = "1";

    /**
     * 资源文件
     */
    private static final String RESOURCENAME = "activiti-cfg.xml";

    /**
     * 节点审批人
     */
    private static final String ASSIGNEE = "assignee";

    /**
     * 实际业务id
     */
    public static final String BUSINESS_KEY = UUID.randomUUID().toString().replace("-", "");
    /**
     * 创建ProcessEngineConfiguration对象
     */
    ProcessEngineConfiguration configuration = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource(RESOURCENAME);

    // todo 第一步：生成activiti7必须的表结构
    @Test
    public void createActivitiTable() {
        // 创建ProcessEngineConfiguration对象 （一种方式）
//        ProcessEngineConfiguration processEngineConfiguration = ProcessEngineConfiguration.createStandaloneProcessEngineConfiguration();
//        processEngineConfiguration.setJdbcDriver("com.mysql.cj.jdbc.Driver");
//        processEngineConfiguration.setJdbcUrl("jdbc:mysql://localhost:3306/platform_wuad?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true");
//        processEngineConfiguration.setJdbcUsername("root");
//        processEngineConfiguration.setJdbcPassword("root");

        // 第二种方式
        ProcessEngineConfiguration processEngineConfiguration = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource(RESOURCENAME);

        //DB_SCHEMA_UPDATE_TRUE 如果表不存在 自动创建表
        processEngineConfiguration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
        //工作流核心对象 processEngine
        ProcessEngine processEngine = processEngineConfiguration.buildProcessEngine();
        log.info("processEngine:"+processEngine);
    }

    
    /**
     * 第二步：流程部署。根据画好的流程文件，bpmn文件和png文件存入数据库中
     */
    @Test
    public void devlopModle() {
        ProcessEngine processEngine = configuration.buildProcessEngine();
        //获取RepositoryService对象
        RepositoryService repositoryService = processEngine.getRepositoryService();
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("process/bank.zip");
        ZipInputStream zipInputStream = new ZipInputStream(inputStream);
        //进行部署
        Deployment deployment = repositoryService
                .createDeployment()
                .addZipInputStream(zipInputStream)
                .name("模拟审批流程")
                // 部署
                .deploy();
        //输出部署的一些信息
        log.info("流程部署id:{}" ,deployment.getId());
        log.info("流程部署名称:{}" ,deployment.getName());
    }

    /**
     * 根据id或流程名称获取流程信息
     */
    @Test
    public void getDevlopModle() {
        ProcessEngine processEngine = configuration.buildProcessEngine();
        //获取RepositoryService对象
        RepositoryService repositoryService = processEngine.getRepositoryService();
        Deployment deployment = repositoryService.createDeploymentQuery().deploymentId(deploymentId).singleResult();
        log.info("流程对象：{}", deployment);
    }

    /**
     * 第三步：根据已部署的流程，创建流程实例
     */
    @Test
    public void beginTest() {
        log.info("开始创建实例");
        String businessKey = BUSINESS_KEY;
        String assignee = "发起人";
        ProcessEngine processEngine = configuration.buildProcessEngine();
        //获取RuntimeService对象
        RuntimeService runtimeService = processEngine.getRuntimeService();
        // 设置流程执行人
        Map<String, Object> variables = new HashMap<>();
        variables.put(ASSIGNEE, assignee);
        //根据流程定义的key启动流程实例（这个id是MyProcess.xml里面的id）
        runtimeService.startProcessInstanceByKey(PROCESS_DEFINITION_KEY, businessKey, variables);
        log.info("流程实例创建完成");
        // 交由分行审批
        disposeTestOne();
    }

    /**
     * 审批流程：发起人通过--->分行审批负责人待审批
     */
    @Test
    public void disposeTestOne() {
        //当前任务负责人
        String assigneeNow = "发起人";
        log.info("将流程交给分行审批负责人");
        // 下一个节点负责人
        String assigneeNext = "我是分行审批负责人";
        //
        String assigneeMsg = "#通过#" + assigneeNow + "提交了申请";
        // 业务关联id
        String businessKey = BUSINESS_KEY;

        ProcessEngine processEngine = configuration.buildProcessEngine();
        //获取TaskService对象
        TaskService taskService = processEngine.getTaskService();
        //查询任务列表，根据流程定义的key和任务负责人
        Task task = taskService.createTaskQuery().processInstanceBusinessKey(businessKey).singleResult();
        log.info("这是任务数据：{}",task);
        // 设置流程执行人
        Map<String, Object> variables = new HashMap<>();
        variables.put(ASSIGNEE, assigneeNext);
        if ( null != task) {
//            // TODO: 2022-02-14 承上启下：对上个节点
            // 添加审批意见
            //需要添加此句否则审批意见表中ACT_HI_COMMENT，审批人的userId是空的
            Authentication.setAuthenticatedUserId(assigneeNow);
            taskService.addComment(task.getId(), task.getProcessInstanceId(), assigneeMsg);
            //完成当前节点的审批
            taskService.complete(task.getId(), variables);
        }
        log.info("此时流程待审批人是分行负责人");
    }

    /**
     * 审批流程：分行负责人审批通过--->总行预审负责人待审批
     */
    @Test
    public void disposeTestTwo() {
        log.info("分行负责人审批，并指定下个节点是总行预审负责人");
        //当前任务负责人
        String assigneeNow = "我是分行审批负责人";
        // 下一个节点负责人
        String assigneeNext = "我是总行预审负责人";
        // 审批意见
        String assigneeMsg = "这是分行审批负责人填的审批意见";
        // 业务关联id
        String businessKey = BUSINESS_KEY;
        ProcessEngine processEngine = configuration.buildProcessEngine();
        //获取TaskService对象
        TaskService taskService = processEngine.getTaskService();
        //查询任务列表，根据流程定义的key和任务负责人
        Task task = taskService.createTaskQuery().processInstanceBusinessKey(businessKey).singleResult();
        log.info("任务列表:{}", task);

        // 设置流程执行人
        Map<String, Object> variables = new HashMap<>();
        variables.put(ASSIGNEE, assigneeNext);
        if ( null != task) {
            String id = task.getId();
            // 添加审批意见
            //需要添加此句否则审批意见表中ACT_HI_COMMENT，审批人的userId是空的
            Authentication.setAuthenticatedUserId(assigneeNow);
            taskService.addComment(id, task.getProcessInstanceId(), assigneeMsg);
            //完成当前节点的审批
            taskService.complete(id, variables);
            log.info("分行负责人审批完成");
        }
    }

    /**
     * 审批流程：行预审负责人审批通过--->总行预审小组待审批
     */
    @Test
    public void disposeTestThree() {
        //当前任务负责人
        String assigneeNow = "我是总行预审负责人";
        // 业务关联id
        String businessKey = BUSINESS_KEY;
        ProcessEngine processEngine = configuration.buildProcessEngine();
        //获取TaskService对象
        TaskService taskService = processEngine.getTaskService();
        //查询任务列表，根据流程定义的key和任务负责人
        List<Task> taskList = taskService.createTaskQuery().processInstanceBusinessKey(businessKey).list();
        // 设置流程执行人：总行预审小组成员
        Map<String, Object> variables = new HashMap<>();
        variables.put("approverList", Arrays.asList("lisi", "wangwu", "zhaoliu"));
        //遍历任务列表
        for (Task task : taskList) {
            String id = task.getId();
            // 添加审批意见
            //需要添加此句否则审批意见表中ACT_HI_COMMENT，审批人的userId是空的
            Authentication.setAuthenticatedUserId(assigneeNow);
            taskService.addComment(id, task.getProcessInstanceId(), "这是总行预审负责人填的审批意见");
            //完成任务
            taskService.complete(id, variables);
        }
    }

    /**
     * 总行预审小组成员全部审批通过才会进入下一节点, 总行预审小组审批通过--->流程发起人（保险信息补录）
     */
    @Test
    public void disposeTestFour() {
        // 当前任务负责人
        String assigneeNow = "wangwu";
        // 业务关联id
        String businessKey = BUSINESS_KEY;
        ProcessEngine processEngine = configuration.buildProcessEngine();
        // 获取TaskService对象
        TaskService taskService = processEngine.getTaskService();
        // 查询任务列表，根据流程定义的key和任务负责人
        Task task = taskService.createTaskQuery().processInstanceBusinessKey(businessKey).taskAssignee(assigneeNow).singleResult();
        if (null != task) {
            String id = task.getId();
            // 添加审批意见
            // 需要添加此句否则审批意见表中ACT_HI_COMMENT，审批人的userId是空的
            Authentication.setAuthenticatedUserId(assigneeNow);
            String commentMsg = "这是" + assigneeNow + "填的审批意见";
            taskService.addComment(id, task.getProcessInstanceId(), commentMsg);

            List<Task> taskList = taskService.createTaskQuery().processInstanceBusinessKey(businessKey).list();
            log.info("1111" + taskList);
            if (taskList.size() == 1) {
                String assigneeNext = "我是流程发起人,也是保险信息补录人";
                // 设置流程执行人：流程发起人（保险信息补录）
                Map<String, Object> variables = new HashMap<>();
                variables.put(ASSIGNEE, assigneeNext);
                taskService.complete(id, variables);
            } else {
                // 完成任务
                taskService.complete(id);
            }
        }
    }

    /**
     * 审批流程：流程发起人（保险信息补录）结束流程
     */
    @Test
    public void finishTest() {
        // 当前任务负责人
        String assigneeNow = "我是流程发起人,也是保险信息补录人";
        // 业务关联id
        String businessKey = BUSINESS_KEY;
        ProcessEngine processEngine = configuration.buildProcessEngine();
        // 获取TaskService对象
        TaskService taskService = processEngine.getTaskService();
        // 查询任务列表，根据流程定义的key和任务负责人
        Task task = taskService.createTaskQuery().processInstanceBusinessKey(businessKey).taskAssignee(assigneeNow).singleResult();
        if (null != task) {
            String id = task.getId();
            // 添加审批意见
            // 需要添加此句否则审批意见表中ACT_HI_COMMENT，审批人的userId是空的
            Authentication.setAuthenticatedUserId(assigneeNow);
            taskService.addComment(id, task.getProcessInstanceId(), "这是" + assigneeNow + "填的审批意见");
            // 完成任务
            taskService.complete(id);
        }
    }

    /**
     * 查询流程：根据任务负责人获取当前待审批任务
     */
    @Test
    public void selectTest() {
        //任务负责人
        String assignee = "我是分行审批负责人";
        ProcessEngine processEngine = configuration.buildProcessEngine();
        //获取TaskService对象
        TaskService taskService = processEngine.getTaskService();
        //查询任务列表，根据流程定义的key和任务负责人
        List<Task> taskList = taskService
                .createTaskQuery()
                .processDefinitionKey(PROCESS_DEFINITION_KEY)
                .taskAssignee(assignee)
                .list();
        log.info("任务列表：{}", taskList);
        //遍历任务列表
        for (Task task : taskList) {
            log.info("任务task：{}", task);
            log.info("流程定义id：{}", task.getProcessDefinitionId());
            log.info("流程实例id：{}", task.getProcessInstanceId());
            log.info("任务负责人：{}", task.getAssignee());
            log.info("任务id：{}", task.getId());
            log.info("任务名称：{}", task.getName());
        }
    }

    /**
     * 获取流程审批记录
     */
    @Test
    public void getHistory() {
        // 业务关联id
        String businessKey = BUSINESS_KEY;
        ProcessEngine processEngine = configuration.buildProcessEngine();
        // 得到历史服务组件
        HistoryService historyService = processEngine.getHistoryService();
        // 查询已完成的流程
        List<HistoricTaskInstance> hisTaskList = historyService.createHistoricTaskInstanceQuery()
                .processInstanceBusinessKey(businessKey)
                .orderByTaskCreateTime()
                .asc()
                .list();

        // 获取TaskService对象
        TaskService taskService = processEngine.getTaskService();
        List<ApprovalHistoryEntity> httpApprovalHistoryList = new ArrayList<>();
        int size = hisTaskList.size();
        for(int i = 0; i<size - 1; i++) {
            ApprovalHistoryEntity approvalHistoryEntity = new ApprovalHistoryEntity();
            HistoricTaskInstance historicTaskInstance  = hisTaskList.get(i);
            List<Comment> comments = taskService.getProcessInstanceComments(historicTaskInstance.getProcessInstanceId());
            comments.forEach(f->{
                if (historicTaskInstance.getProcessInstanceId().equals(f.getProcessInstanceId())) {
                    approvalHistoryEntity.setApprovalOpinion(f.getFullMessage());
                }
            });
            approvalHistoryEntity.setAgree(historicTaskInstance.getAssignee());
            approvalHistoryEntity.setApprovalTime(historicTaskInstance.getEndTime());
            httpApprovalHistoryList.add(approvalHistoryEntity);
            log.info("这是个啥:{}", historicTaskInstance);
        }
        log.info("qqqqq:{}",httpApprovalHistoryList);
    }

    /**
     * 流程回退到上个节点
     */
    @Test
    public void returnLastOne() throws Exception {
        // 业务关联id
        String businessKey = BUSINESS_KEY;
        // 当前节点负责人
        String approverNowCode = "wangwu";
        String commentMsg = "填的审批意见";
        ProcessEngine processEngine = configuration.buildProcessEngine();
        TaskService taskService = processEngine.getTaskService();
        Task task = taskService.createTaskQuery()
                .processInstanceBusinessKey(businessKey)
                .taskAssignee(approverNowCode)
                .singleResult();
        String lastTaskAssignee = backProcess(task, commentMsg);
        log.info("lastTaskAssignee:{}",lastTaskAssignee);
    }

    public String backProcess(Task task, String commentMsg) {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        HistoryService historyService = processEngine.getHistoryService();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        TaskService taskService = processEngine.getTaskService();
        String processInstanceId = task.getProcessInstanceId();
        // 查询已完成的流程
        List<HistoricTaskInstance> hisTaskList = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId)
                .orderByTaskCreateTime()
                .desc()
                .list();
        List<HistoricTaskInstance> hisTaskList1 = new ArrayList<>();
        hisTaskList.stream().filter(distinctByKey(p -> p.getTaskDefinitionKey()))
                .forEach(hisTaskList1::add);

        List<HistoricActivityInstance> hisActivityList = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId).list();

        if (CollectionUtils.isEmpty(hisTaskList) || hisTaskList.size() < 2) {
            return null;
        }
        //  前一个任务
        HistoricTaskInstance lastTask = hisTaskList1.get(1);
        //  当前活动
        HistoricActivityInstance currentActivity = hisActivityList.stream().filter(e -> task.getId().equals(e.getTaskId())).collect(Collectors.toList()).get(0);
        //  前一个活动
        HistoricActivityInstance lastActivity = hisActivityList.stream().filter(e -> lastTask.getId().equals(e.getTaskId())).collect(Collectors.toList()).get(0);
        BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
        //  获取前一个活动节点
        FlowNode lastFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(lastActivity.getActivityId());
        //  获取当前活动节点
        FlowNode currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(currentActivity.getActivityId());
        //  临时保存当前活动的原始方向
        List<SequenceFlow> originalSequenceFlowList = new ArrayList<>();
        originalSequenceFlowList.addAll(currentFlowNode.getOutgoingFlows());
        //  清理活动方向
        currentFlowNode.getOutgoingFlows().clear();
        //  建立新方向
        SequenceFlow newSequenceFlow = new SequenceFlow();
        newSequenceFlow.setId("newSequenceFlowId");
        newSequenceFlow.setSourceFlowElement(currentFlowNode);
        newSequenceFlow.setTargetFlowElement(lastFlowNode);
        List<SequenceFlow> newSequenceFlowList = new ArrayList<>();
        newSequenceFlowList.add(newSequenceFlow);
        //  当前节点指向新的方向
        currentFlowNode.setOutgoingFlows(newSequenceFlowList);
        // 需要添加此句否则审批意见表中ACT_HI_COMMENT，审批人的userId是空的
        Authentication.setAuthenticatedUserId(task.getAssignee());
        taskService.addComment(task.getId(), task.getProcessInstanceId(), "这是" + task.getAssignee() + commentMsg);
        //  完成当前任务
        taskService.complete(task.getId());
        //  重新查询当前任务
        List<Task> nextTask = taskService.createTaskQuery().processInstanceId(processInstanceId).list();
        if(!nextTask.isEmpty()) {
            int size = nextTask.size();
            for(int i=0;i<size;i++) {
                if(size == 1 && !StringUtils.equals("usertask4", nextTask.get(i).getTaskDefinitionKey())) {
                    taskService.setAssignee(nextTask.get(i).getId(), lastTask.getAssignee());
                } else {
                    taskService.complete(nextTask.get(i).getId());
                }
            }
        }
        //  恢复原始方向
        currentFlowNode.setOutgoingFlows(originalSequenceFlowList);
        return lastTask.getAssignee();
    }

    static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
        Map<Object,Boolean> seen = new ConcurrentHashMap<>();
        //putIfAbsent方法添加键值对，如果map集合中没有该key对应的值，则直接添加，并返回null，如果已经存在对应的值，则依旧为原来的值。
        //如果返回null表示添加数据成功(不重复)，不重复(null==null :TRUE)
        return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }

    /**
     * 获取某人审批过的数据
     */
    @Test
    public void getSomeBodyHistory() {
        // 某人id
        String assignee = "我是分行审批负责人";
        ProcessEngine processEngine = configuration.buildProcessEngine();
        // 得到历史服务组件
        HistoryService historyService = processEngine.getHistoryService();
        // 查询已完成的流程
        List<HistoricTaskInstance> hisTaskList = historyService.createHistoricTaskInstanceQuery()
                .taskAssignee(assignee)
                .orderByTaskCreateTime()
                .desc()
                .list();
        //历史实例过程查询
        List<HistoricProcessInstance> historicProcessInstancesList = historyService.createHistoricProcessInstanceQuery()
                .processInstanceIds(hisTaskList.stream().map(TaskInfo::getProcessInstanceId).collect(Collectors.toSet()))
                .list();

        List<String> businessKeys = new ArrayList<>();
        for(HistoricProcessInstance historicProcessInstance : historicProcessInstancesList) {
            businessKeys.add(historicProcessInstance.getBusinessKey());
        }
        log.info("12121212:" + businessKeys);
    }

}
